'/
' RabNet (Rabid Networking)
' 
' This is a strimmed down BlitzMax port of RakNet. It is basically inspired
' by RakNet, but is very strimmed down, and a bunch of new additional
' features has been added, all written from scratch.
' 
' Copyright (c) 2003 Jenkins Software LLC.
' RabNet written 2010-2011 by Anton Berlin.
' 
' =========================================================================
' 
'                                Version 1.1
' 
' =========================================================================
' 
' All new features is free to use in any way, as long as you inlude
' Anton Berlin (or Anthony Berlin) as the orinial writer of the code.
' 
' All old features are subject to the appropriate license agreement.
' Creative Commons Licensees are subject to the license found at
' http://creativecommons.org/licenses/by-nc/2.5/
' 
' Single application licensees are subject to the license found at
' http://www.jenkinssoftware.com/SingleApplicationLicense.html
' 
' Custom license users are subject to the terms therein.
' 
' GPL license users are subject to the GNU General Public
' License as published by the Free Software Foundation; either version 3
' of the License, or (at your option) any later version.
'/

superstrict

import BRL.LinkedList
import BRL.Map
import BRL.Socket
import BRL.SocketStream
import BRL.Reflection
import BRL.System
import BRL.KeyCodes
import BRL.Random

import "../../framework/Base/Base.bmx"
import "../RabMT/RabMT.bmx"
import "../strutil/strutil.bmx"

const USE_RAB_ASSERT:byte = 1; '// 0 = no, 1 = yes, 2 = use default blitz assert.
const USE_DEBUG:byte = false;

function DEBUG(str:string)

	if (USE_DEBUG) ..
		writestdout("RabNet DEBUG: " + str + "~n");

end function

function rabAssert(condition:byte)

	select (USE_RAB_ASSERT)
	
		case 0
			return;
		case 1
			if (not condition) then
			
				notify("Unhandled exception: RabNet error.", true);
				end;
			
			end if
		case 2
			assert(condition);
	
	end select

end function

include "RabPeer.bmx"
include "RabNetworkFactory.bmx"
include "RabNetSocket.bmx"
include "SystemAddress.bmx"
include "Packet.bmx"

'// Main RabNet namespace. It contains some functions not related to the internals of RabNet, and most of the constants
'// used both internally by RabNet, and externally by the programmer using RabNet.
type RabNet

	'// -----------------------------------------------------------------------------------------------------------------
	'// Description:
	'//     Unassigned stuff.
	'// -----------------------------------------------------------------------------------------------------------------
	global UNASSIGNED_SYSTEM_ADDRESS:SystemAddress = new SystemAddress;
	global UNASSIGNED_RABNET_GUID:int = -1;
	
	'// -----------------------------------------------------------------------------------------------------------------
	'// Description:
	'//     Message identifiers that are used internally.
	'// -----------------------------------------------------------------------------------------------------------------
	global ID_NEW_INCOMING_CONNECTION:byte = 1;
	global ID_CONNECTION_REQUEST_ACCEPTED:byte = 2;
	global ID_CONNECTION_ATTEMPT_FAILED:byte = 3;
	global ID_NO_FREE_INCOMING_CONNECTIONS:byte = 4;
	global ID_DISCONNECTION_NOTIFICATION:byte = 5;
	global ID_CONNECTION_LOST:byte = 6;
	global ID_INTERNAL_PING:byte = 7;
	global ID_CONNECTED_PONG:byte = 8;
	
	'// -----------------------------------------------------------------------------------------------------------------
	'// Description:
	'//     User-defined message identifiers.
	'// -----------------------------------------------------------------------------------------------------------------
	global ID_USER_DEFINED:byte = 20;
	
	'// -----------------------------------------------------------------------------------------------------------------
	'// Description:
	'//     Script packet id. This one is not used internally by RabNet. You can delete it if you want, since it is just
	'//     another packet id. It is supposed to send data that use keywords and arguments, for the cases where this is
	'//     important. Beware of doing that too often though, because it is a very slow technique.
	'// -----------------------------------------------------------------------------------------------------------------
	global ID_SCRIPT:byte = 255;
	
	'// -----------------------------------------------------------------------------------------------------------------
	'// Description:
	'//     Return the hostname of the local system. The Local System is the system the application running RabNet was
	'//     launched on. It does not rely on any internal objects, but on system information from the OS.
	'// -----------------------------------------------------------------------------------------------------------------
	function getLocalSystemHostName:string()
	
		return hostname(hostip("localhost", 0));
	
	end function
	
	'// -----------------------------------------------------------------------------------------------------------------
	'// Description:
	'//     Return the local IP address of the local system.
	'// -----------------------------------------------------------------------------------------------------------------
	function getLocalSystemInternalIP:string()
	
		return hostip("localhost", 0);
	
	end function
	
	'// -----------------------------------------------------------------------------------------------------------------
	'// Description:
	'//     Return the remote ip address of a specific host name on the internet.
	'// 
	'// Parameters:
	'//     (string) host_name: The name of the host from which we wish to retrieve an ip address.
	'// -----------------------------------------------------------------------------------------------------------------
	function getRemoteIPFromHostName:int(host_name:string)
	
		return hostip(host_name);
	
	end function
	
	'// -----------------------------------------------------------------------------------------------------------------
	'// Description:
	'//     Send a message to another peer.
	'// 
	'// Parameters:
	'//     (RabPeer) peer: The peer that sends the packet.
	'//     (string) msg: The data contained inside the packet.
	'//     (SystemAddress) target: The target system.
	'// -----------------------------------------------------------------------------------------------------------------
	function sendMessageToPeer(peer:RabPeer, msg:string, target:SystemAddress)
	
		if (peer and msg and target) ..
			peer.send(msg, target, false);
	
	end function
	
	'// -----------------------------------------------------------------------------------------------------------------
	'// Description:
	'//     Send a message to all connected peers.
	'// 
	'// Parameters:
	'//     (RabPeer) peer: The peer that sends the packet.
	'//     (string) msg: The data contained inside the packet.
	'// -----------------------------------------------------------------------------------------------------------------
	function sendMessageToAll(peer:RabPeer, msg:string)
	
		if (peer and msg) ..
			peer.send(msg, peer.getThisSystemAddress(), true);
	
	end function
	
	'// -----------------------------------------------------------------------------------------------------------------
	'// Description:
	'//     Send a message to all connected peers, including yourself, except one system.
	'// 
	'// Parameters:
	'//     (RabPeer) peer: The peer that sends the packet.
	'//     (string) msg: The data contained inside the packet.
	'//     (SystemAddress) exception: The one system that should not receive the packet.
	'// -----------------------------------------------------------------------------------------------------------------
	function sendMessageToAllButOne(peer:RabPeer, msg:string, exception:SystemAddress)
	
		if (peer and msg and exception) ..
			peer.send(msg, exception, true);
	
	end function
	
	'// -----------------------------------------------------------------------------------------------------------------
	'// Description:
	'//     Prevent this type to be instantiated.
	'// -----------------------------------------------------------------------------------------------------------------
	method new()
	
		local id:string = TTypeId.forObject(self).Name();
		runtimeError("Illegal to create an instance of a namespace class '" + id + "'.");
	
	end method
	
	'// -----------------------------------------------------------------------------------------------------------------
	'// Description:
	'//     The highest known available port.
	'// -----------------------------------------------------------------------------------------------------------------
	global m_highestKnownAvailablePort:int = 0;

end type
